/*****************************************************************************
*    Copyright (C)2003 Ali Corporation. All Rights Reserved.
*
*    File:    nim_mt312.h
*
*    Description:    This file contains mt312 basic function in LLD.
*    History:
*           Date            Athor        Version          Reason
*	    ============	=============	=========	=================
*	1.	Jun.12.2003     George jiang     Ver 0.1    Create file.
*	2.	Aug.22.2003     Justin Wu        Ver 0.2    Optimize driver.
*	3.	Oct.17.2003		Justin Wu		 Ver 0.3	Support MT10312.
*													Support +/-4MHz fix freq
*													   search.
*****************************************************************************/

#include <sys_config.h>
#include <retcode.h>
#include <types.h>
#include <osal/osal.h>
#include <api/libc/alloc.h>
#include <api/libc/printf.h>
#include <api/libc/string.h>
#include <hal/hal_gpio.h>
#include <bus/i2c/i2c.h>
#include <hld/hld_dev.h>
#include <hld/nim/nim_dev.h>
#include <hld/nim/nim.h>
#include <hld/nim/nim_tuner.h>
#include "nim_mt312.h"

#define NIM_PRINTF(...)
#define SWITCH_NIM_MT312_DEBUG	0

static INT32 nim_mt312_open(struct nim_device *dev);
static INT32 nim_mt312_close(struct nim_device *dev);
static INT32 nim_mt312_ioctl(struct nim_device *dev, INT32 cmd, UINT32 param);
static INT32 nim_mt312_set_polar(struct nim_device *dev, UINT8 polar);
static INT32 nim_mt312_set_12v(struct nim_device *dev, UINT8 flag);
static INT32 nim_mt312_channel_change(struct nim_device *dev, UINT32 freq, UINT32 sym, UINT8 fec);
static INT32 nim_mt312_channel_search(struct nim_device *dev, UINT32 freq);
static INT32 nim_mt312_DiSEqC_operate(struct nim_device *dev, UINT32 mode, UINT8* cmd, UINT8 cnt);

static INT32 nim_mt312_get_lock(struct nim_device *dev, UINT8 *lock);
static INT32 nim_mt312_get_freq(struct nim_device *dev, UINT32 *freq);
static INT32 nim_mt312_get_symbol_rate(struct nim_device *dev, UINT32 *sym_rate);
static INT32 nim_mt312_get_code_rate(struct nim_device *dev, UINT8* code_rate);
static INT32 nim_mt312_get_AGC(struct nim_device *dev, UINT8 *agc);
static INT32 nim_mt312_get_SNR(struct nim_device *dev, UINT8 *snr);
static INT32 nim_mt312_get_PER(struct nim_device *dev, UINT32 *RsUbc);

static INT32 nim_mt312_tuner_lock(struct nim_device *dev, UINT8 *tun_lock);
static INT32 nim_mt312_get_FEC_lock(struct nim_device *dev, UINT8 *fec_lock);
static INT32 nim_mt312_ioctl_ext(struct nim_device *dev, INT32 cmd, void* param_list);
static void nim_mt312_i2c_pass(struct nim_device *, int);
static INT32 nim_mt312_autoscan(struct nim_device *dev, struct NIM_Auto_Scan* pstAuto_Scan);


struct nim_mt312_private
{
	struct   QPSK_TUNER_CONFIG_DATA Tuner_Config_Data;
	INT32 	(*nim_Tuner_Init) (UINT32*, struct QPSK_TUNER_CONFIG_EXT *);	/* Tuner Initialization Function */
	INT32 	(*nim_Tuner_Control) (UINT32, UINT32, UINT32);	/* Tuner Parameter Configuration Function */
	INT32 	(*nim_Tuner_Status) (UINT32, UINT8 *);		
	UINT32 tuner_id;
	UINT32 i2c_type_id;
	UINT32 polar_gpio_num;
	UINT32 sys_crystal;
	UINT32 sys_clock;
	UINT16 pre_freq ;
	UINT16 pre_sym ;
	INT8 autoscan_stop_flag ;
	struct nim_device_stats stats;
	UINT8 chip_id;
};

/* Name for the tuner, the last character must be Number for index */
static char nim_mt312_name[HLD_MAX_NAME_SIZE] = "NIM_MT312_0";

static const unsigned char vit_ref[] =
{
    0x34, 0x80, 0x14, 0x0A, 0x06, 0x04, 0x02, 0x01
//    0x14, 0x12, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF
};


static INT32 nim_mt312_read(UINT32 i2c_type_id,UINT8 dev_add, UINT8 reg_add, UINT8 *data, UINT8 len)
{
	INT32 err;

	data[0] = reg_add;
	err = i2c_write_read(i2c_type_id, dev_add, data, 1, len);

	return err;
}

static INT32 nim_mt312_write(UINT32 i2c_type_id,UINT8 dev_add, UINT8 reg_add, UINT8 *data, UINT8 len)
{
	UINT8  i, buffer[8];

	if (len > 7)
	{
	    return ERR_FAILUE;
	}
	buffer[0] = reg_add;
	for (i = 0; i < len; i++)
	{
	    buffer[i + 1] = data[i];
	}

	return i2c_write(i2c_type_id, dev_add, buffer, len + 1);
}

INT32 nim_mt312_ioctl_ext(struct nim_device *dev, INT32 cmd, void* param_list)
{
	PRINTF("	Enter fuction nim_mt312_ioctl_ext\n");
	switch( cmd )
	{
	case NIM_DRIVER_AUTO_SCAN:			/* Do AutoScan Procedure */
		return nim_mt312_autoscan(dev, (struct NIM_Auto_Scan *) (param_list));
	case NIM_DRIVER_CHANNEL_CHANGE:		/* Do Channel Change */
	{
		struct NIM_Channel_Change * nim_param = (struct NIM_Channel_Change *) (param_list);
		return nim_mt312_channel_change(dev, nim_param->freq, nim_param->sym, nim_param->fec);
	}
	case NIM_DRIVER_CHANNEL_SEARCH:	/* Do Channel Search */
		break;
	default:
	    break;
	}
	
	return SUCCESS;
}
/*****************************************************************************
* INT32  nim_mt312_init()
* Description: MT312 initialization
*
* Arguments:
*  none
*
* Return Value: INT32
*****************************************************************************/
INT32 nim_mt312_attach(struct QPSK_TUNER_CONFIG_API * tuner_config_api)
{
	struct nim_device *dev;
	struct nim_mt312_private  *priv_mem;

	dev = (struct nim_device *)dev_alloc(nim_mt312_name, HLD_DEV_TYPE_NIM, sizeof(struct nim_device));
	if (dev == NULL)
	{
		PRINTF("Error: Alloc nim device error!\n");
		return ERR_NO_MEM;
	}

	/* Alloc structure space of private */
	priv_mem = (struct nim_mt312_private  *)MALLOC(sizeof(struct nim_mt312_private));
	if (priv_mem == NULL)
	{
		dev_free(dev);
		PRINTF("Alloc nim device prive memory error!/n");
		return ERR_NO_MEM;
	}
	MEMSET((UINT8 *)priv_mem, 0, sizeof(struct nim_mt312_private));
	dev->priv = (void *)priv_mem;

	/* Function point init */
	//dev->base_addr = 0xff;
	dev->init = nim_mt312_attach;
	dev->open = nim_mt312_open;
	dev->stop = nim_mt312_close;
	dev->do_ioctl = nim_mt312_ioctl;
	dev->set_polar = nim_mt312_set_polar;
	dev->set_12v = nim_mt312_set_12v;
	//dev->channel_change = nim_mt312_channel_change;
	dev->do_ioctl_ext = nim_mt312_ioctl_ext;
	dev->channel_search = nim_mt312_channel_search;
	dev->DiSEqC_operate = nim_mt312_DiSEqC_operate;
	dev->get_lock = nim_mt312_get_lock;
	dev->get_freq = nim_mt312_get_freq;
	dev->get_sym = nim_mt312_get_symbol_rate;
	dev->get_FEC = nim_mt312_get_code_rate;
	dev->get_AGC = nim_mt312_get_AGC;
	dev->get_SNR = nim_mt312_get_SNR;
	dev->get_BER = nim_mt312_get_PER;

	priv_mem->nim_Tuner_Init = tuner_config_api->nim_Tuner_Init;
	priv_mem->nim_Tuner_Control = tuner_config_api->nim_Tuner_Control;
	priv_mem->nim_Tuner_Status = tuner_config_api->nim_Tuner_Status;
	priv_mem->Tuner_Config_Data.Recv_Freq_Low = tuner_config_api->config_data.Recv_Freq_Low;
	priv_mem->Tuner_Config_Data.Recv_Freq_High = tuner_config_api->config_data.Recv_Freq_High;
	priv_mem->Tuner_Config_Data.Ana_Filter_BW = tuner_config_api->config_data.Ana_Filter_BW;
	priv_mem->Tuner_Config_Data.Connection_config = tuner_config_api->config_data.Connection_config;
	priv_mem->Tuner_Config_Data.AGC_Threshold_1 = tuner_config_api->config_data.AGC_Threshold_1;
	priv_mem->Tuner_Config_Data.AGC_Threshold_2 = tuner_config_api->config_data.AGC_Threshold_2;
	priv_mem->Tuner_Config_Data.QPSK_Config = tuner_config_api->config_data.QPSK_Config;
			
	dev->base_addr = tuner_config_api->ext_dm_config.i2c_base_addr;
	priv_mem->sys_crystal = tuner_config_api->ext_dm_config.dm_crystal;
	priv_mem->i2c_type_id = tuner_config_api->ext_dm_config.i2c_type_id;
	priv_mem->polar_gpio_num = tuner_config_api->ext_dm_config.polar_gpio_num;
	priv_mem->sys_clock = tuner_config_api->ext_dm_config.dm_clock;
				
	/* Add this device to queue */
	if (dev_register(dev) != SUCCESS)
	{
		PRINTF("Error: Register nim device error!\n");
		FREE(priv_mem);
		dev_free(dev);
		return ERR_NO_DEV;
	}	
	if (priv_mem->nim_Tuner_Init)
	{
		NIM_PRINTF(" %s: Initial the Tuner \n", __FUNCTION__);

		if (priv_mem->nim_Tuner_Init (&priv_mem->tuner_id, &(tuner_config_api->tuner_config)) != SUCCESS)
		{
			NIM_PRINTF("Error: Init Tuner Failure!\n");
			return ERR_NO_DEV;
		}
	}

	return SUCCESS;
}


/*****************************************************************************
* INT32 nim_mt312_hw_init(struct nim_device *dev)
* Description: MT312 hardware initialization
*
* Arguments:
*  Parameter1: struct nim_device *dev
*
* Return Value: void
*****************************************************************************/
static INT32 nim_mt312_hw_init(struct nim_device *dev)
{
	UINT8  data;
	struct nim_mt312_private* priv = (struct nim_mt312_private  *)dev->priv;
	/* Full reset here */
	data = 0x80;
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_RESET, &data, 1);
	/* update for MT10313 */
	if((((struct nim_mt312_private *)dev->priv)->chip_id) == NIM_MT312_ID_MT10313)
	{
		data = 0;
		nim_mt312_write(priv->i2c_type_id, dev->base_addr,NIM_MT312_HW_CTRL, &data, 1);
	}
	if (priv->sys_crystal== 4000) /* PLL2 bit is set to '1' */
	{
		data = 0x02;
		nim_mt312_write(priv->i2c_type_id, dev->base_addr,NIM_MT312_HW_CTRL, &data, 1);
	}
	/* setup AGC referance */
	if (priv->chip_id == NIM_MT312_ID_MT10313)
	{
		if (priv->Tuner_Config_Data.AGC_Threshold_1 == 0x40) //for zl10039+zl10313
		{
			data = 0x40;
		}
		else
		{
			data = 0xaa;
		}
	}
	else
	{
		data = 0x67;
	}
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_FE_AGC_REF, &data, 1);

	/* Set up system clock frequency register */
	data = ((priv->sys_clock/1000) << 1);
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_SYS_CLK, &data, 1);

	/* Set frequency limit, +nMHz ~ -nMHz */
	data = NIM_MT312_FREQ_SEARCH_RANG * 8;
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_FR_LIM, &data, 1);

	/* Set up DiSEqC ratio */
	data = (priv->sys_crystal+(4*22-1))/(4*22);
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_DISEQC_RATIO, &data, 1);

	/* Change SNR_THS_HIGH */
	data = 50;
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_SNR_THS_HIGH, &data, 1);

	/* Set up DiSEqC mode */
	/* Set up symbol rate */
	/* Set up code rate, pay attention bit6 */
	data = NIM_MT312_TM_IQ_INV ? 0x40 : 0;
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_VIT_MODE, &data, 1);

	/* Set up defaults for viterbi */
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_VIT_SETUP, \
	      (unsigned char *)vit_ref, sizeof(vit_ref));

	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_MON_CTRL, &data, 1);
	data |= 0x80;
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_MON_CTRL, &data, 1);

#if 0 /* Those code is used to set TS output format, it is for test only */
	/* For SPI output format setup */
	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_OP_CTRL, &data, 1);
	data |= 0x08;	/* For serial SPI output */
//	data |= 0x40;	/* For ERR invert output */
//	data &= 0xdf;	/* For MCLK invert output */
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_OP_CTRL, &data, 1);

	/* For manual bitrate output setup */
//	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_FEC_STAT_EN, &data, 1);
//	data &= 0x0f;
//	data |= 0x00;	/* PLL_CLK / (RATIO + 6) */
//	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_FEC_STAT_EN, &data, 1);

//	data = 0x43;
//	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_FEC_SETUP, &data, 1);
#endif
	/* DEBUG: set up rx IQ swap, AFC_M and ROLL_20 */
	data = (NIM_MT312_HW_IQ_INV ? 0x40 : 0) | 0x04;
	return nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_QPSK_CTRL, &data, 1);
}

/*****************************************************************************
* INT32 nim_mt312_open(struct nim_device *dev)
* Description: MT312 open
*
* Arguments:
*  Parameter1: struct nim_device *dev
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_open(struct nim_device *dev)
{
	struct nim_mt312_private *priv = (struct nim_mt312_private *)dev->priv;
	UINT8 data;

	/* Get MT312 chip ID: MT312 is 0x03, MT10312 is 0x04 */
	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_ID, &(priv->chip_id), 1);
	PRINTF("nim_mt312_init: MT312 ID is 0x%02x\n", priv->chip_id);

	data = 0x80 | (priv->sys_crystal== 15000 ? 2 : 0);
	switch (priv->sys_clock/ priv->sys_crystal)
	{
	case 3:
		data |= 0x00;
		break;
	case 4:
		data |= 0x04;
		break;
	case 6:
	case 23:/* for 92M clock & 4M crystal */
		data |= 0x08;
		break;
	case 9:
		data |= 0x0c;
		break;
	default:
		break;
	}

	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_CONFIG, &data, 1);

	osal_delay(150);		/* Spec: 150uS before write MT312 RESET */
	nim_mt312_hw_init(dev);

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_close(struct nim_device *dev)
* Description: MT312 close
*
* Arguments:
*  Parameter1: struct nim_device *dev
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_close(struct nim_device *dev)
{
	UINT8  data;
	struct nim_mt312_private *priv = (struct nim_mt312_private*)dev->priv;
	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_CONFIG, &data, 1);
	data &= 0x7f;
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_CONFIG, &data, 1);

	return SUCCESS;
}

static void nim_mt312_i2c_pass(struct nim_device *dev, int on_off)
{
	struct nim_mt312_private *priv = (struct nim_mt312_private *)dev->priv;
	UINT8 data;

	if (on_off)
	{
		if ((priv->chip_id == NIM_MT312_ID_MT10312) || \
			  (priv->chip_id == NIM_MT312_ID_MT10313))
			data = 0xC0;
		else
			data = 0x40;
	} else
	{
		if ((priv->chip_id == NIM_MT312_ID_MT10312) || \
              (priv->chip_id == NIM_MT312_ID_MT10313))
			data = 0x80;
		else
			data = 0x00;
	}
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_GPP_CTRL, &data, 1);
}

/*****************************************************************************
* INT32 nim_mt312_set_polar(struct nim_device *dev, UINT8 polar)
* Description: MT312 set polarization
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8 pol
*
* Return Value: void
*****************************************************************************/
static INT32 nim_mt312_set_polar(struct nim_device *dev, UINT8 polar)
{
	UINT8 val;
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;
	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_DISEQC_MODE, &val, 1);
	HAL_GPIO_BIT_DIR_SET(priv->polar_gpio_num, HAL_GPIO_O_DIR);
	switch (polar)
	{
	case NIM_PORLAR_HORIZONTAL:
		HAL_GPIO_BIT_SET(priv->polar_gpio_num, 0);
	    val |= DISEQC_REG_HORIZ;	/* If use MT312 pin, this active polar */
		break;
	case NIM_PORLAR_VERTICAL:
		HAL_GPIO_BIT_SET(priv->polar_gpio_num, 1);
	    val &= ~DISEQC_REG_HORIZ;	/* If use MT312 pin, this active polar */
		break;
	default:
        break;
	}

	return nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_DISEQC_MODE, &val, 1);
}

/*****************************************************************************
* INT32 nim_mt312_set_12v(struct nim_device *dev, UINT8 flag)
* Description: MT312 set LNB votage 12V enable or not
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8 flag
*
* Return Value: SUCCESS
*****************************************************************************/
static INT32 nim_mt312_set_12v(struct nim_device *dev, UINT8 flag)
{
	return SUCCESS;
}
/*****************************************************************************
* INT32 nim_mt312_autoscan(struct nim_device *dev, struct NIM_Auto_Scan* pstAuto_Scan);
* Description: mt312 auto scan operation
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: struct NIM_Auto_Scan* pstAuto_Scan		: pointer to the AutoScan parameter list
*
* Return Value: INT32
*****************************************************************************/
//INT32  api_nim_autoscan(UINT32 sfreq, UINT32 efreq, INT32 (*callback)(UINT8 status, UINT8 polar, UINT32 freq, UINT32 sym, UINT8 fec))
static INT32 nim_mt312_autoscan(struct nim_device *dev, struct NIM_Auto_Scan* pstAuto_Scan)
{
#if 0
	UINT8 cur_polar=0, cur_fec;
	UINT32 cur_freq, cur_sym, step;
	UINT32 nosig_count;
	int polar_loop, freq_loop;
	int locked;
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;

	if (pstAuto_Scan->sfreq < 950 || pstAuto_Scan->efreq > 2150 || pstAuto_Scan->sfreq > pstAuto_Scan->efreq)
	{
		return ERR_FAILUE;
	}
	nim_mt312_ioctl(dev, NIM_DRIVER_STOP_ATUOSCAN, 0);
	PRINTF("api_nim_autoscan: Begin autoscan\n");
	/* Polarity loop */
	for (polar_loop = 0; polar_loop < 2; polar_loop++)
	{
		switch (polar_loop)
		{
		case 0:
			cur_polar = NIM_PORLAR_HORIZONTAL;
			break;
		default:
			cur_polar = NIM_PORLAR_VERTICAL;
			break;
		}
		nim_mt312_set_polar(dev, cur_polar);

		step = 5;
		nosig_count = 4;
		/* Frequence loop */
		for (cur_freq = pstAuto_Scan->sfreq; cur_freq < pstAuto_Scan->efreq; cur_freq += step)
		{
			/* From dem_mt312_as.c:
			 * If no freq found, then
			 *	 New freq y = last freq x + 2/3 Rsmin (i.e. channel spacing)
			 *	 Chip scan width = 1/2 of 2/3 Rsmin
			 * If freq found, then
			 *	 New freq y = x + 2/3Rs(found) / 2 + 2/3Rsmin/2
			 *	 Chip scan width = 1/2 of 2/3 Rsmin
			 * Or... (I can't sure which algrithm should be used -- Justin Wu)
			 *   Channel spacing seems to be about 0.69Rs (e.g. 19MHz for 27.5MBd)
			 *   or more precisely f2 = f1 + 0.345Rs1 +0.345Rs2 perhaps?
			 *   Next frequency to scan from would therefore be
			 *   f2 = f1 + 0.345Rs1 + 0.345Rse where
			 *   f1 is the found frequency
			 *   Rs1 is its symbol rate
			 *   Rse is the (min) symbol rate being searched for
			 */
			if(priv->autoscan_stop_flag)
			{
				return !SUCCESS;
			}
			if (nosig_count > 3)
				locked = nim_mt312_channel_search(dev, \
				      cur_freq | 0x80000000) == SUCCESS;
			else
				locked = nim_channel_search(dev, \
				      cur_freq) == SUCCESS;
			if (locked)
			{
				nim_mt312_get_freq(dev, &cur_freq);
				nim_mt312_get_symbol_rate(dev, &cur_sym);
				nim_mt312_get_code_rate(dev, &cur_fec);
				step = ((cur_sym * 33 / 1000) + 99) / 100 + 2;
				nosig_count = 0;
			}
			else
			{
				if (++nosig_count > 3)
					step = 5;
				else
					step = 2;			/* If not any channel, jump 1 * 2 + 1 MHz */
			}

		    switch (pstAuto_Scan->callback(locked, cur_polar, cur_freq, cur_sym, cur_fec))
		    {
		    case 0:					/* Command: continue */
		    	break;
		    case 1:					/* Command: exit */
				pstAuto_Scan->callback(2, cur_polar, cur_freq, 0, 0);
		    	return SUCCESS;
		    	break;
		    case 2:					/* Command: reset */
		    	cur_freq = pstAuto_Scan->efreq;
		    	polar_loop = -1;
		    	break;
		    default:
		    	break;
		    }
		}
	}
	pstAuto_Scan->callback(2, cur_polar, cur_freq, 0, 0);	/* Tell callback search finished */
	PRINTF("api_nim_autoscan: Finish autoscan\n");

	return SUCCESS;
#endif

	UINT8 cur_polar = 0, cur_fec;
	UINT32 tmp_freq, cur_freq, cur_sym, step;

	int polar_loop, freq_loop;
	int locked;
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;

	if (pstAuto_Scan->sfreq < 950 || pstAuto_Scan->efreq > 2150 || pstAuto_Scan->sfreq > pstAuto_Scan->efreq)
	{
		return ERR_FAILUE;
	}
	nim_mt312_ioctl(dev, NIM_DRIVER_STOP_ATUOSCAN, 0);
	PRINTF("api_nim_autoscan: Begin autoscan\n");
	/* Polarity loop */
	for (polar_loop = 0; polar_loop < 1; polar_loop++)
	{
/*
		switch (polar_loop)
		{
		case 0:
			cur_polar = NIM_PORLAR_HORIZONTAL;
			break;
		case 1:
			cur_polar = NIM_PORLAR_VERTICAL;
			break;
		default:
			break;
		}
		nim_set_polar(dev, cur_polar);
*/
		cur_polar = NIM_PORLAR_HORIZONTAL;


		/* Frequence loop */
		for (cur_freq = pstAuto_Scan->sfreq; cur_freq < pstAuto_Scan->efreq; cur_freq += step)
		{
			/* From dem_mt312_as.c:
			 * If no freq found, then
			 *	 New freq y = last freq x + 2/3 Rsmin (i.e. channel spacing)
			 *	 Chip scan width = 1/2 of 2/3 Rsmin
			 * If freq found, then
			 *	 New freq y = x + 2/3Rs(found) / 2 + 2/3Rsmin/2
			 *	 Chip scan width = 1/2 of 2/3 Rsmin
			 * Or... (I can't sure which algrithm should be used -- Justin Wu)
			 *   Channel spacing seems to be about 0.69Rs (e.g. 19MHz for 27.5MBd)
			 *   or more precisely f2 = f1 + 0.345Rs1 +0.345Rs2 perhaps?
			 *   Next frequency to scan from would therefore be
			 *   f2 = f1 + 0.345Rs1 + 0.345Rse where
			 *   f1 is the found frequency
			 *   Rs1 is its symbol rate
			 *   Rse is the (min) symbol rate being searched for
			 */
			if(priv->autoscan_stop_flag)
			{
				return !SUCCESS;
			}


			if (nim_channel_search(dev, cur_freq) == SUCCESS)

			{
				locked = 1;
				tmp_freq = cur_freq;
				nim_get_freq(dev, &cur_freq);
				nim_get_sym(dev, &cur_sym);
				nim_get_FEC(dev, &cur_fec);
			    step = ((cur_sym / 1000) * 67 + 99) / 100 + 1;
			    if (tmp_freq <= cur_freq)
			    {
					if (step < 2)		/* For frequence search range is +/-1MHz */
					{
						step = 2;
					}
			    } else
			    {
			    	if (step < 3)
			    	{
			    		step = 3;
			    	}
			    }
			}else
			{
				locked = 0;
			    step = 3;			/* If not any channel, jump 1 * 2 + 1 MHz */


			}

		    switch (pstAuto_Scan->callback(locked, cur_polar, cur_freq, cur_sym, cur_fec))
		    {
		    case 0:					/* Command: continue */
		    	break;
		    case 1:					/* Command: exit */
				pstAuto_Scan->callback(2, cur_polar, cur_freq, 0, 0);
		    	return SUCCESS;
		    	break;
		    case 2:					/* Command: reset */
		    	cur_freq =  pstAuto_Scan->efreq;
		    	polar_loop = -1;
		    	break;
		    default:
		    	break;
		    }
		}
	}
//	pstAuto_Scan->callback(2, cur_polar, cur_freq, 0, 0);	/* Tell callback search finished */
	PRINTF("api_nim_autoscan: Finish autoscan\n");

	return SUCCESS;


}
/*****************************************************************************
* INT32 nim_mt312_channel_change(struct nim_device *dev, UINT32 freq, UINT32 sym, UINT8 fec);
* Description: MT312 channel change operation
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT32 freq				: Frequence
*  Parameter3: UINT32 sym				: Symbol rate
*  Parameter4: UINT8 fec				: Code rate
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_channel_change(struct nim_device *dev, UINT32 freq, UINT32 sym, UINT8 fec)
{
	UINT8 data[2];
	UINT8 low_sym;
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;
	if(freq == priv->pre_freq && sym == priv->pre_sym)
		return SUCCESS;
	priv->pre_freq = freq;
	priv->pre_sym = sym;
	low_sym = sym < 10000 ? 1 : 0;	/* Symbol rate is less than 10M, low symbol rate */

	/* Setup tuner */
	nim_mt312_i2c_pass(dev, 1);
	priv->nim_Tuner_Control(priv->tuner_id, freq, sym);
	nim_mt312_i2c_pass(dev, 0);

	/* Set H/V in DiSEqC mode register according to nim_channel_stats */
	/* Write symbol rate according to nim_channel_stats */
	data[0]= (UINT8)((sym / 1000) & 0x3f);	/* sym / 1000 * 256 >> 8 */
	data[1]= (UINT8)((sym << 8) / 1000);	/* sym / 1000 * 256 */
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_SYM_RATE, data, 2);

	/* write viterbi mode register, DEBUG: Transmitter IQ inversion */
	data[0] = NIM_MT312_TM_IQ_INV ? 0x40 : 0;
	switch (fec)
	{
	case 1:
		data[0] |= 0x01;
		break;
	case 2:
		data[0] |= 0x02;
		break;
	case 3:
		data[0] |= 0x04;
		break;
	case 5:
		data[0] |= 0x08;
		break;
	case 6:
		data[0] |= 0x10;
		break;
	case 7:
		data[0] |= 0x20;
		break;
	default:		/* Auto search code rate */
		data[0] |= 0xbf;
		break;
	}
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_VIT_MODE, data, 1);

	data[0] = 0x01;
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_GO, data, 1);

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_channel_search(struct nim_device *dev, UINT32 freq);
* Description: MT312 channel change operation
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT32 freq					: Frequence
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_wait_QPSK_lock(struct nim_device *dev, UINT8 *qpsk_lock)
{
	UINT8 data[4];
	UINT32 search_done, qpsk_lmt, agc_lmt;
	struct nim_mt312_private * priv = (struct nim_mt312_private*)dev->priv;
	*qpsk_lock = 0;
	if (nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_SYM_RATE_H, data, 2) != SUCCESS)
	{
		return ERR_FAILUE;
	}
	search_done = ((data[0] & 0x80) == 0x80) ? NIM_MT312_BAUD_SRCH_DONE : NIM_MT312_FREQ_SRCH_DONE;
#if 0
	/* <3.8S for HW QPSK search with 2M frequence jump and no <1.8M Rs search, we just set 4S for timeout */
	for (qpsk_lmt = 0; qpsk_lmt < 4000; qpsk_lmt++)
#else
	/* Timeout is 1.5S for QPSK lock, for low Rs, and 30mS for AGC lock */
	for (agc_lmt = 0, qpsk_lmt = 0; agc_lmt < 6 && qpsk_lmt < 300; agc_lmt++, qpsk_lmt++)
#endif
	{
		if(priv->autoscan_stop_flag)
			return SUCCESS;

		if (nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_QPSK_INT_L, data, 4) != SUCCESS)
		{
			return ERR_FAILUE;
		}
		if ((data[2] & NIM_QPSK_LOCK_TEST) == NIM_QPSK_LOCK_TEST)	/* QPSK locked */
		{
			*qpsk_lock = 1;
			break;
		}
		if ((data[0] & search_done) == search_done)		/* Search finished */
		{
			break;
		}
		if (data[3] & NIM_MT312_QPSK_SWEEP_ON)	/* check for QPSK timing sweep on */
		{
			agc_lmt = 0;	/* Disable agc timer limit check */
		}
		osal_task_sleep(10);
	}

	return SUCCESS;
}


static INT32 nim_mt312_channel_search(struct nim_device *dev, UINT32 freq)
{
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;
	UINT8 data[2], lock_status;
	int i;

	/* Clear last remembered status */
	priv->pre_freq = 0;
	priv->pre_sym = 0;

	/* Set frequency search limit for autoscan, +nMHz ~ -nMHz */
	if (freq & 0x80000000)
	{
		data[0] = 8* 8;
		freq &= 0x7fffffff;
	}
	else
		data[0] = 2*8;

	/* Setup tuner. We must lock tuner first, else QPSK maybe lost some channels */
	nim_mt312_i2c_pass(dev, 1);
	priv->nim_Tuner_Control(priv->tuner_id, freq, 0);//??
	nim_mt312_i2c_pass(dev, 0);

	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_FR_LIM, data, 1);

	/* write symbol rate according to system clock
	 * now system clock is set to 90MHz, and search range is from 1.8 t0 45 MHz
	 * Lower Rs cost most more time for QPSK lock, so we don't search <1.8M Rs.
	 */
#if 0//(SYS_TUN_MODULE == MAX2118)
	data[0] = 0x8f;		/* Auto search for all Rs */
	data[1] = 0xF0;
//	if((freq > 1050)&&(freq < 1750))
//		data[1] = 0xF8;
#else
	data[0] = 0xBF;
	data[1] = 0xFF;
#endif
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_SYM_RATE, data, 2);

	/* write viterbi mode register for search */
	data[0] = 0xbf | (NIM_MT312_TM_IQ_INV ? 0x40 : 0);
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_VIT_MODE, data, 1);

	/* Wait tuner lock, MAX to 10mS. We have test this time is 1mS ~3mS in fact */
	for (i = 0; i < NIM_MT312_TUN_LOCK_TIME; i++)
	{
		if(priv->autoscan_stop_flag)
		{
			/* Set frequency search limit for channel change, +nMHz ~ -nMHz */
			data[0] = NIM_MT312_FREQ_SEARCH_RANG * 8;
			nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_FR_LIM, data, 1);
			return !SUCCESS;
		}

		nim_mt312_tuner_lock(dev, &lock_status);
		if (lock_status == 1)
		{
			break;
		}
		osal_delay(1000);
	}

	if (lock_status)
	{
		data[0] = 0x01;
		nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_GO, data, 1);

		/* Wait QPSK lock. We found this time is more depend on both of frequence and Rs search range */
		nim_mt312_wait_QPSK_lock(dev, &lock_status);
		/* Wait FEC lock, MAX to 200mS for Hi Rs and 50mS for Lw Rs.
		 * We have test this time is 65mS. */
		if (lock_status == 1)
		{
			for (i = 0; i < NIM_MT312_FEC_LOCK_TIME; i++)
			{
				if(priv->autoscan_stop_flag)
				{
					/* Set frequency search limit for channel change, +nMHz ~ -nMHz */
					data[0] = NIM_MT312_FREQ_SEARCH_RANG * 8;
					nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_FR_LIM, data, 1);
					return !SUCCESS;
				}
				nim_mt312_get_FEC_lock(dev, &lock_status);
				if (lock_status == 1)
				{
					/* Set frequency search limit for channel change, +nMHz ~ -nMHz */
					data[0] = NIM_MT312_FREQ_SEARCH_RANG * 8;
					nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_FR_LIM, data, 1);
					return SUCCESS;
				}
				osal_delay(1000);
			}
		}
	}
	/* Set frequency search limit for channel change, +nMHz ~ -nMHz */
	data[0] = NIM_MT312_FREQ_SEARCH_RANG * 8;
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_FR_LIM, data, 1);

	return ERR_FAILUE;
}

/*****************************************************************************
* INT32 nim_mt312_DiSEqC_operate(struct nim_device *dev, UINT32 mode, UINT8* cmd, UINT8 cnt)
*
*  defines DiSEqC operations
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT32 mode
*  Parameter3: UINT8* cmd
*  Parameter4: UINT8 cnt
*
* Return Value: void
*****************************************************************************/
INT32 nim_mt312_DiSEqC_operate(struct nim_device *dev, UINT32 mode, UINT8* cmd, UINT8 cnt)
{
	UINT8 data, temp;
	UINT16 timeout, timer;
	struct nim_mt312_private* priv = (struct nim_mt312_private*)dev->priv;
	switch (mode)
	{
	case NIM_DISEQC_MODE_22KOFF:
	case NIM_DISEQC_MODE_22KON:
		nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_DISEQC_MODE, &data, 1);
		data = ((data & ~DISEQC_REG_TONEBITS) | mode);
		nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_DISEQC_MODE, &data, 1);
		break;
	case NIM_DISEQC_MODE_BURST0:
	    nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_DISEQC_MODE, &data, 1);
	    //tone burst 0
	    temp = 0x02;
	    data |= temp;
	    nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_DISEQC_MODE, &data, 1);
	    osal_delay(16000);
	    break;
	case NIM_DISEQC_MODE_BURST1:
	 nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_DISEQC_MODE, &data, 1);
        //tone bust 1
	    temp = 0x03;
	    data |= temp;
	    nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_DISEQC_MODE, &data, 1);
	    osal_delay(16000);
	    break;
	case NIM_DISEQC_MODE_BYTES:
		nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_DISEQC2_CTRL1, &data, 1);
		data |= 0x10;
		nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_DISEQC2_CTRL1, &data, 1);

		nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_DISEQC_INSTR + IAI, cmd, cnt);
	    temp = (((cnt - 1) & 0x07) << 3) + 4;
		nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_DISEQC_MODE, &data, 1);
		data &= 0xc0;
		data |= temp;
		nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_DISEQC_MODE, &data, 1);

		timer = 0;
		timeout = 75 + 13 * cnt;
		nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_FEC_INT, &data, 1);
		while (timer < timeout)
		{
			nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_FEC_INT, &data, 1);
			if (data & 0x80)
			{
				break;
			}
			osal_delay(10000);
			timer += 10;
		}
		break;
	default :
		break;
	}

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_get_FreqOffset(struct nim_device *dev, INT32* offsetFreq
*
*  Reads the NIM and returns the frequency offset in KHz
*  This function calculates the offset from ERR1 and ERR2, and is therefore
*  accurate immediately after lock.
*     Ferr = -(Err1*SYS_CLK +Err2*RsMHz)/131
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: INT32* offsetKhzFreq
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_get_FreqOffset(struct nim_device *dev, INT32 *offsetFreq)
{
	UINT8 data[5];
	UINT32 sym_rate;
	INT16 freqErr1, freqErr2;
	INT32 tempFreq = 0;
	struct nim_mt312_private * priv = (struct nim_mt312_private*)dev->priv;
	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_FREQ_ERR, data, 5);
	freqErr1 = (data[0] << 8) + data[1];
	freqErr2 = (data[3] << 8) + data[4];

	nim_mt312_get_symbol_rate(dev, &sym_rate);

	/* This algrithm is from dem_mt312_as.c. -- Justin Wu */
	if ((((struct nim_mt312_private *)dev->priv)->chip_id) == NIM_MT312_ID_MT312)
	tempFreq = -((INT32)((freqErr1 * (priv->sys_clock/1000) * 2) + (freqErr2 * (sym_rate / 1000))) / 131);
    else
#if (SYS_TUN_MODULE == SL1935 || SYS_TUN_MODULE == SL1935D)
	tempFreq = (INT32)((freqErr1 * (priv->sys_clock/1000) * 2) + (freqErr2 * (sym_rate / 1000))) / 131;
#elif (SYS_TUN_MODULE == ZL10036)
		tempFreq = -((INT32)((freqErr1 * (priv->sys_clock/1000) * 2) + (freqErr2 * (sym_rate / 1000))) / 131);
#endif
	*offsetFreq = tempFreq / 1000;

#if	SWITCH_NIM_MT312_DEBUG
	PRINTF("freqErr1=%08d, freqErr2=%08d, offest freq=%05dMhz\t",
		freqErr1, freqErr2, *offsetFreq);
#endif
	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_get_freq(struct nim_device *dev, UINT32 *freq)
* Read MT312 frequence
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16 *sym_rate			: Symbol rate in kHz
*
* Return Value: void
*****************************************************************************/
static INT32 nim_mt312_get_freq(struct nim_device *dev, UINT32 *freq)
{
	INT32 freq_off;

	nim_mt312_get_FreqOffset(dev, &freq_off);
	*freq = (INT32)(*freq) + freq_off;

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_get_symbol_rate(struct nim_device *dev, UINT32 *sym_rate)
* Read MT312 symbol rate
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16 *sym_rate			: Symbol rate in kHz
*
* Return Value: void
*****************************************************************************/
static INT32 nim_mt312_get_symbol_rate(struct nim_device *dev, UINT32 *sym_rate)
{
	UINT8  buf[2];
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;
	/* set monitor control regiter to 3 */
	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_MON_CTRL, buf, 1);
	buf[0] = (buf[0] & 0xF0 | NIM_MON_SYMRATE);
	nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_MON_CTRL, buf, 1);

	/* Symbol rate in kHz */
	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_MONITOR, buf, 2);
	*sym_rate = (UINT32)((((buf[0] << 8) + buf[1]) * 1000) >> 10) ;/*By David 2004.02.11*/
//	*sym_rate -= (*sym_rate%10);
	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_get_code_rate(struct nim_device *dev, UINT8* code_rate)
* Description: Read MT312 code rate
*   FEC status (b6-b4)  code rate  return value
*	0					1/2			1
*	1					2/3			2
*	2					3/4			3
*	3					5/6			5
*	4					6/7			6
*	5					7/8			7
*	6					invalid		8
*	7					invalid		9
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8* code_rate
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_get_code_rate(struct nim_device *dev, UINT8 *code_rate)
{
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;
	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_FEC_STATUS, code_rate, 1);
	*code_rate = ((*code_rate >> 4) & 0x07) + 1;
	if (*code_rate >= 4)
	{
		*code_rate += 1;
	}

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_get_AGC(struct nim_device *dev, UINT8 *agc)
*
*  This function will access the NIM to determine the AGC feedback value
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16* agc
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_get_AGC(struct nim_device *dev, UINT8 *agc)
{
	UINT8 data[2];
	UINT32 temp;
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;
	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_AGC, data, 2);
	temp = ((data[0] << 6) + (data[1] >> 2));
	*agc = ((temp * 100) >> 14);		/* Limit within 100 */

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_get_SNR(struct nim_device *dev, UINT8 *snr)
*
* This function returns an approximate estimation of the SNR from the NIM
*  The Eb No is calculated using the SNR from the NIM, using the formula:
*     Eb ~     13312- M_SNR_H
*     -- =    ----------------  dB.
*     NO           683
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16* RsUbc
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_get_SNR(struct nim_device *dev, UINT8 *snr)
{
	UINT8 data[2];
	INT32 temp;
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;
	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_M_SNR, data, 2);
	temp = (((data[0] & 0x7f) << 8) + data[1]);
	if (temp > 13312)
	{
		temp = temp - 13312;
	} else
	{
		temp = 13312 - temp;
	}
	*snr = 13 + temp /200;	/* Make it MAX value near 100 */

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_tuner_lock(struct nim_device *dev, UINT8 *tun_lock)
*
*  Read tuner lock status
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8 *tun_lock
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_tuner_lock(struct nim_device *dev, UINT8 *tun_lock)
{
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;
	UINT8 data;

	/* Setup tuner */
	nim_mt312_i2c_pass(dev, 1);
	priv->nim_Tuner_Status(priv->tuner_id, tun_lock);
	nim_mt312_i2c_pass(dev, 0);

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_get_QPSK_lock(struct nim_device *dev, UINT8 *qpsk_lock)
*
*  Read QPSK lock status
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8 *qpsk_lock
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_get_QPSK_lock(struct nim_device *dev, UINT8 *qpsk_lock)
{
	UINT8  qpsk_stat[2];
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;
    *qpsk_lock = 0;
	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_STATUS, qpsk_stat, 2);
	if ((qpsk_stat[0] & NIM_QPSK_LOCK_TEST) == NIM_QPSK_LOCK_TEST)
	{
	    *qpsk_lock = 1;
	}
//	PRINTF_TEST("qpsk_stat = %2x\n",qpsk_stat[0]);
	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_get_FEC_lock(struct nim_device *dev, UINT8 *fec_lock)
*
*  Read FEC lock status
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: BOOL *fec_lock
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_get_FEC_lock(struct nim_device *dev, UINT8 *fec_lock)
{
	UINT8 fec_stat;
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;
	*fec_lock = 0;
	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_FEC_STATUS, &fec_stat, 1);
	if ((fec_stat & NIM_FEC_LOCK_TEST) == NIM_FEC_LOCK_TEST)
	{
	    *fec_lock = 1;
	}
//	PRINTF_TEST("fec_stat = %2x\n",fec_stat);
	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_get_lock(struct nim_device *dev, UINT8 *lock)
*
*  Read FEC lock status
*
*Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: BOOL *fec_lock
*
*Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_get_lock(struct nim_device *dev, UINT8 *lock)
{
	UINT8 fec_lock, qpsk_lock;

	nim_mt312_get_QPSK_lock(dev, &qpsk_lock);
	nim_mt312_get_FEC_lock(dev, &fec_lock);
	if((fec_lock && qpsk_lock) || qpsk_lock)
		*lock=1;
	else
		*lock=0;
	//*lock = (fec_lock && qpsk_lock) ? 1 : 0;
	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_get_QPSKBER(struct nim_device *dev, UINT32* qpsk_ber)
* Uses the viterbi error correction mechanism to
* determine the error rate out of the QPSK
* retrun value is 1E6 * the bit error rate
* error rate (*1e6)  = VIT_ERRCNT /(VIT_ERRPER *4)   *1e6
* error rate (*1e6) = VIT_ERRCNT *250000/VIT_ERRPER
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT32* qpsk_ber
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_get_QPSKBER(struct nim_device *dev, UINT32* qpsk_ber)
{
	UINT8   data[4];
	UINT32  ErrorCount, ErrorPeriod;
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;
	
	*qpsk_ber =0xffffffff;
	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_VIT_ERRCNT, data, 3);
	ErrorCount = (((data[0] << 8) + data[1]) << 8) + data[2];

	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_VIT_ERRPER, data, 3);
	ErrorPeriod = (((data[0] << 8) + data[1]) << 8) + data[2];

	/* calculate the result */
	if (ErrorCount <= UINT32_MAX / 25000)
	{
		ErrorCount *= 25000;
		ErrorPeriod = (ErrorPeriod + 5) / 10;
	} else if (ErrorCount <= UINT32_MAX / 2500)
	{
		ErrorCount *=2500;
		ErrorPeriod = (ErrorPeriod + 50) / 100;
	} else
	{
		ErrorCount *=250;
		ErrorPeriod = (ErrorPeriod + 500) / 1000;
	}
	if (ErrorPeriod > 0)
	{
		*qpsk_ber = ErrorCount / ErrorPeriod;
	}

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_get_VitBER(struct nim_device *dev, UINT32 *vitber, UINT16 time)
*  Vitber = RS_BERCNT/(time *Rs * CR * 2)
*  time is in  seconds up to 65535 (18 hours)
*  a time value of zero causes the registers to be read (reset)
*  and the return value is set to 0xffffffff (= 4.29)
*  The values normally are very small, hence the return value is
*  multiplied by 1E9, hence the formula becomes:
*                                    RS_BERCNT *1000 * 1E6
*  vitber (*1E9) =      __________________________________________________________
*                        time * (symbolratex256*1E6/256) * coderate/(coderate+1) *x
*
*  simplified, and modified for integer arithmetic:
*
*                                    RS_BERCNT *256000
*  vitber (*1E9) =      _________________________________________________
*                        time * symbolratex256 * coderate/(coderate+1) *x
*  result			 1E-9		4E-1
*  then denominator thus has a minimum value of 5*120*.5*1 = 300
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT32 *vitber
*  Parameter3: UINT16 time
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_get_VitBER(struct nim_device *dev, UINT32 *vitber, UINT16 time)
{
	UINT8  vit_err[3], coderate;
	UINT32 sym_rate;
	UINT32 numerator, denominator, sym_temp;
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;
	nim_mt312_get_code_rate(dev, &coderate);
	nim_mt312_get_symbol_rate(dev, &sym_rate);
	denominator = coderate * time * (sym_rate * 256 / 1000) * 2 / (coderate + 1);

	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_RS_BERCNT, vit_err, 3);
	numerator = (((vit_err[0] << 8) + vit_err[1]) << 8) + vit_err[2];
	if (numerator <= UINT32_MAX / 256000)
	{
		numerator *= 256000;
	} else if (numerator <= UINT32_MAX / 25600)
	{
		numerator *=25600;
		denominator = (denominator + 5) / 10;
	} else if (numerator <= UINT32_MAX / 2560)
	{
		numerator *= 2560;
		denominator = (denominator + 50) / 100;
	} else if (denominator < 5000)
	{
		/* error count is greater than Uint32T_MAX/2560 clip it */
		numerator = UINT32_MAX / 2560;
		denominator = (denominator + 50) / 100;
	} else
	{
		numerator *=256;
		denominator = (denominator + 500) / 1000;
	}

	/* post the answer and return */
	if (denominator > 0)
	{
		*vitber = numerator / denominator;
	} else
	{
		*vitber = 0xFFFFFFFF;
	}

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_get_PER(struct nim_device *dev, UINT32 *RsUbc)
* Reed Solomon Uncorrected block count
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16* RsUbc
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_get_PER(struct nim_device *dev, UINT32 *RsUbc)
{
	UINT8 data[2];
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;
	nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_RS_UBC, data, 2);
	*RsUbc = (data[0] << 8) + data[1];

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mt312_ioctl(struct nim_device *dev, INT32 cmd, UINT32 param)
*
*  device input/output operation
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: INT32 cmd
*  Parameter3: UINT32 param
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mt312_ioctl(struct nim_device *dev, INT32 cmd, UINT32 param)
{
	struct nim_mt312_private * priv = (struct nim_mt312_private *)dev->priv;
	UINT8 data,lock;
	switch( cmd )
	{
	case NIM_DRIVER_READ_TUNER_STATUS:
	    return nim_mt312_tuner_lock(dev, (UINT8 *)param);
	    break;
	case NIM_DRIVER_READ_QPSK_STATUS:
	    return nim_mt312_get_QPSK_lock(dev, (UINT8 *)param);
	    break;
	case NIM_DRIVER_READ_FEC_STATUS:
	    return nim_mt312_get_FEC_lock(dev, (UINT8 *)param);
	    break;
	case NIM_DRIVER_READ_QPSK_BER:
	    return nim_mt312_get_QPSKBER(dev, (UINT32 *)param);
	    break;
	case NIM_DRIVER_READ_VIT_BER:
	    /* here the called function need a parameter time */
	    return nim_mt312_get_VitBER(dev, (UINT32 *)param, VIT_BER_PER);
	    break;
	case NIM_DRIVER_READ_RSUB:
	    return nim_mt312_get_PER(dev, (UINT32 *)param);
	    break;
	case NIM_DRIVER_STOP_ATUOSCAN:
		priv->autoscan_stop_flag = param;
		break;
	case NIM_DRIVER_TS_OUTPUT:
		nim_mt312_get_lock(dev,&lock);
		if(lock)
		{
			nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_OP_CTRL, &data, 1);
			data = data&0x40?data&0xBF:data|0x40;
			nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_OP_CTRL, &data, 1);
		}
		break;
	case NIM_DRIVER_SET_TS_MODE:
		nim_mt312_read(priv->i2c_type_id, dev->base_addr, NIM_MT312_OP_CTRL, &data, 1);
		if(param&0x1)
			data |= 0x08;	/* For serial output */
		else
			data &= 0xf7;/* For parallel output */
		nim_mt312_write(priv->i2c_type_id, dev->base_addr, NIM_MT312_OP_CTRL, &data, 1);
		break;
	default:
	    break;
	}
	return SUCCESS;
}
